#!python
# Parsing.
import re

index_pattern = re.compile(r'(?:(?P<vnum>\d+)\.zon)|\$$')

def parseindex(index_name):
    ##    return [int(i) for i in
    ##            map(lambda n:index_pattern.match(n).groupdict()['vnum'],
    ##                open(index_name).readlines()) \
    ##            if type(i) is str and i.isdigit()]

    # Something more verbose, to debug:
    index = []
    for line in open(index_name).readlines():
        match = index_pattern.match(line)
        if match is not None:
            vnum = match.groupdict()['vnum']
            if type(vnum) is str and vnum.isdigit():
                index.append(int(vnum))

    return index

def segment(array, nr):
    nr = int(nr)

    ln = len(array)
    step = ln / nr

    assert step

    ##    print 'ln: %s, nr: %s, step: %s' % (ln, nr, step)
    ##    print array

    x = 0
    while (ln - x) >= step:
        ##    print 'x: %s, x+step: %s: ln-x: %s, array[x:x+step]: %s' % \
        ##          (x, x+step, ln-x, array[x:x+step])

        yield array[x:x+step]
        x += step

    yield array[x:]

def nr_segments(total_size, max_size, allowance):
    return total_size / ((1 - float(allowance)) * max_size)

# Build.
def writeIndex(filename, index):
    fl = open(filename, 'w')
    for vnum in index:
        fl.write('%d.zon\n' % vnum)

    fl.write('$\n')
    fl.flush()
    fl.close()

from os.path import dirname, basename, join as joinpath, isdir, isfile
from os import mkdir, system
from tempfile import gettempdir

def getDirectory(base, *parts):
    if not isdir(base) and base:
        mkdir(base)

    for d in parts:
        base = joinpath(base, d)
        if not isdir(base):
            mkdir(base)

    return base

def getBuildSegmentBase(build_name, nr):
    return getDirectory(gettempdir(), build_name, '%04d' % nr)

##    def getZoneSpan(base, vnum):
##        # Todo: build this once!
##        from stuphlib.wldlib import WorldLoader
##        from stuphlib.dblib import LocalFileIndex
##        loader = WorldLoader(LocalFileIndex(base))
##
##        zone = loader.createZone(vnum)
##        return zone.span

def copy_file(src, dest):
    if isfile(src):
        system('/bin/cp "%s" "%s"' % (src, dest))

def remove_directory(path):
    system('/bin/rm -Rf "%s"' % path)

def zip_archive(name, source):
    print 'zip_archive: %r -> %r' % (source, name)

    # Zip up contents without containing segmentation folder.
    system('cd "%s"; zip -c "%s" .' % (source, name))
    # system('zip -c "%s" "%s"' % (name, source))

FIXES = ('zon', 'wld', 'obj', 'mob', 'shp', 'sch')

def prepare_segment((build_name, world_base, index_name, archive_base), (segment, nr)):
    #
    #  This builds a temporary directory and then zips it to file.
    #  Todo: compile zip archive directly without intermediate fs.
    #
    try:
        # Rebuild a world index segment.
        build_base = getBuildSegmentBase(build_name, nr)
        print 'build_base: %r' % build_base

        # Build parts containers.
        parts = {}
        for fix in FIXES:
            parts[fix] = getDirectory(build_base, fix)

        # Build zon index.
        writeIndex(joinpath(parts['zon'], index_name), segment)

        # This is not true: Spanned contents go into one file!
        ##    # Must load the zone record to discover zone span!
        ##    span = getZoneSpan(world_base, vnum)

        # Build parts.
        for fix in FIXES:
            for vnum in segment:
                this = '%d.%s' % (vnum, fix)
                copy_file(joinpath(world_base, fix, this),
                          joinpath(parts[fix], this))

        # Zip Archive.
        zip_archive(joinpath(archive_base, '%04d.zip' % nr),
                    build_base)

    finally:
        # Remove temporary build directories.
        remove_directory(build_base)

def prepare_segmented_index(index_file, build_name,
                            (total_size, max_size, allowance)):

    world_base = dirname(dirname(index_file))
    index_name = basename(index_file)

    archive_base = getDirectory(world_base, build_name)

    total = nr_segments(total_size, max_size, allowance)
    nr = 1

    for seg in segment(parseindex(index_file), total):
        prepare_segment((build_name, world_base, index_name, archive_base),
                        (seg, nr))
        nr += 1

# Test -- Calculate index segments where each (average) size
# will fit within maximum allowed size, with room to spare.
TOTAL_SIZE = 4676642
MAX_SIZE = (1 << 20)
ALLOWANCE = 0.25

# INDEX_FILE = r'H:/StuphMUD/lib/world/zon/index'

BUILD_NAME = 'stuphworld-segments'
BUILD_PARTS = [BUILD_NAME] # 'only'

def parse_cmdln(argv):
    from optparse import OptionParser
    parser = OptionParser()
    return parser.parse_args(argv)

def main(argv = None):
    (options, args) = parse_cmdln(argv)
    index_file = args[0]

    this_build = '-'.join(BUILD_PARTS)
    prepare_segmented_index(index_file, this_build,
                            (TOTAL_SIZE, MAX_SIZE, ALLOWANCE))

if __name__ == '__main__':
    main()
